using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;

namespace ReportMaintenanceUtility
{
	/// <summary>
	/// A DataSetReference encapsulates the name, label and value fields used by a report parameter
	/// to generate its list of available or default values.
	/// </summary>
	public class DataSetReference : GenericBase<DataSetReference>
	{
		#region PRIVATE MEMBER(S)
		private DataSetReferenceType			_seed		= null;
		private string							_name		= string.Empty;
		private string							_label		= string.Empty;
		private string							_value		= string.Empty;
		private DataSet							_target		= null;
		private Dictionary<string, DataSet>		_dsLibrary	= null;
		#endregion

		#region PUBLIC PROPERTIES

		/// <summary>
		/// The name of the referenced DataSet
		/// </summary>
		public string Name
		{
			get { return this._name; }
		}
		
		/// <summary>
		/// The name of the field returned by the referenced dataset the parameter should use as the label when generating its values list
		/// </summary>
		public string LabelField
		{
			get { return this._label; }
			set
			{
				this.PrepStateChange("LabelField");
				this._label = value;
			}
		}
		
		/// <summary>
		/// /// The name of the field returned by the referenced dataset the parameter should use as the value when generating its values list
		/// </summary>
		public string ValueField
		{
			get { return this._value; }
			set
			{
				this.PrepStateChange("ValueField");
				this._value = value;
			}
		}
		
		/// <summary>
		/// A pointer to the referenced DataSet object, which is defined and stored at the Report Level
		/// </summary>
		public DataSet Target
		{
			get { return this._target; }
			set
			{
				this.PrepStateChange("Target");
				if (value == null)
				{
					this._name = string.Empty;
					this._label = string.Empty;
					this._value = string.Empty;
				}
				else
				{
					this._name = value.Name;
				}
				this._target = value;
			}
		}
		#endregion

		#region CONSTRUCTOR(S)
		
		/// <summary>
		/// Private constructor used by a DataSetReference to create deep copies of itself
		/// </summary>
		/// <param name="dsr">The source DataSetReference on which to base the new copy</param>
		private DataSetReference(DataSetReference dsr)
		{
			if (dsr == null)
			{
				throw new ArgumentNullException();
			}
			else
			{
				this._seed = (DataSetReferenceType)dsr.CreateSeedCopy();
				this._dsLibrary = dsr._dsLibrary;
				base.CopyListeners(dsr);
				this.Initialize();
			}
		}

		/// <summary>
		/// Creates a DataSetReference from a deserialized RDL DataSetReferenceType object
		/// </summary>
		/// <param name="dsrt"></param>
		/// <param name="datasets"></param>
		public DataSetReference(DataSetReferenceType dsrt, Dictionary<string, DataSet> datasets)
		{
			if (dsrt == null)
			{
				throw new ArgumentNullException();
			}
			else
			{
				this._seed = dsrt;
				this._dsLibrary = datasets;
				this.Initialize();
			}
		}

		protected override void Initialize()
		{
			for (int i = 0; i < this._seed.ItemsElementName.Length; i++)
			{
				switch (this._seed.ItemsElementName[i])
				{
					case ItemsChoiceType31.DataSetName:
						this._name = this._seed.Items[i].ToString();
						foreach (DataSet dst in this._dsLibrary.Values)
						{
							if (dst.Name.Equals(this._name, StringComparison.CurrentCultureIgnoreCase))
							{
								this._target = dst;
								this._target.PropertyChanged += this.PropertyChangedListener;
								break;
							}
						}
						break;
					case ItemsChoiceType31.LabelField:
						this._label = this._seed.Items[i].ToString();
						break;
					case ItemsChoiceType31.ValueField:
						this._value = this._seed.Items[i].ToString();
						break;
				}
			}
		}
		#endregion

		#region INTERFACE IMPLMENTATION(S)

		/// <summary>
		/// Compares the properties and values of two DataSetReference objects to determine if they are equivalent
		/// </summary>
		/// <param name="other">The DataSetReference object to compare the current instance to</param>
		/// <returns>True if the objects are equivalent</returns>
		public override bool EqualTo(DataSetReference other)
		{
			if (other == null)
			{
				return false;
			}
			else if (this._label != other._label || this._value != other._value)
			{
				return false;
			}
			else if (this.Target == null && other.Target == null)
			{
				return (this._name.Equals(other._name, StringComparison.CurrentCultureIgnoreCase));
			}
			else if (this._target != null && this._target.EqualTo(other._target))
			{
				return true;
			}
			return false;
		}

		/// <summary>
		/// Copies any changes made in the DataSetReference object to its underlying seed object, calls the SaveState() method of
		/// any child objects it contains, and resets the IsDirty flag to false.
		/// </summary>
		/// <returns>List of all changes made within the DataSetReference object and its children</returns>
		public override List<ChangeLog> SaveState()
		{
			List<ChangeLog> log = new List<ChangeLog>();

			if (this._isDirty)
			{
				int idx = -1;

				List<ItemsChoiceType31> elements = null;

				//label
				if (this._label != this._copy._label)
				{
					elements = new List<ItemsChoiceType31>(this._seed.ItemsElementName);
					idx = elements.IndexOf(ItemsChoiceType31.LabelField);

					if (string.IsNullOrEmpty(this._label))
					{
						if (idx > -1)
						{
							this.RemoveSeedItem(elements, idx);
						}
					}
					else
					{
						if (idx > -1) //if prompt element already exists, then simply overwrite with the new value
						{
							this._seed.Items[idx] = this._label;
						}
						else //add a new element to the underlying seed object...
						{
							this.AddSeedItem(elements, ItemsChoiceType31.LabelField, this._label);
						}
					}
					log.Add(new ChangeLog(ChangeType.DataSetReferenceLabelFieldChange, this._copy._label, this._label));
				}

				//value
				if (this._value != this._copy._value)
				{
					elements = new List<ItemsChoiceType31>(this._seed.ItemsElementName);
					idx = elements.IndexOf(ItemsChoiceType31.ValueField);

					if (string.IsNullOrEmpty(this._value))
					{
						if (idx > -1)
						{
							this.RemoveSeedItem(elements, idx);
						}
					}
					else
					{
						if (idx > -1) //if prompt element already exists, then simply overwrite with the new value
						{
							this._seed.Items[idx] = this._value;
						}
						else //add a new element to the underlying seed object...
						{
							this.AddSeedItem(elements, ItemsChoiceType31.ValueField, this._value);
						}
					}
					log.Add(new ChangeLog(ChangeType.DataSetReferenceValueFieldChange, this._copy._value, this._value));
				}

				//dataset name
				if (this._name != this._copy._name)
				{
					elements = new List<ItemsChoiceType31>(this._seed.ItemsElementName);
					idx = elements.IndexOf(ItemsChoiceType31.DataSetName);

					if (string.IsNullOrEmpty(this._name))
					{
						if (idx > -1)
						{
							this.RemoveSeedItem(elements, idx);
						}
					}
					else
					{
						if (idx > -1) //if prompt element already exists, then simply overwrite with the new value
						{
							this._seed.Items[idx] = this._name;
						}
						else //add a new element to the underlying seed object...
						{
							this.AddSeedItem(elements, ItemsChoiceType31.DataSetName, this._name);
						}
					}
					log.Add(new ChangeLog(ChangeType.DataSetName, this._copy._name, this._name));
				}

				//target
				if (this._target != null)
				{
					ChangeLog sub = new ChangeLog(ChangeType.DataSetReferenceTargetChange, this._copy._target, this._target);
					sub.RelatedChanges.AddRange(this._target.SaveState());
					log.Add(sub);
				}
			}

			this.ClearState();
			return log;
		}

		private void AddSeedItem(List<ItemsChoiceType31> elements, ItemsChoiceType31 element, string value)
		{
			if (!elements.Contains(element))
			{
				elements.Add(element);

				List<object> items = new List<object>(this._seed.Items);
				items.Add(value);

				this._seed.ItemsElementName = elements.ToArray();
				this._seed.Items = items.ToArray();
			}
		}

		private void RemoveSeedItem(List<ItemsChoiceType31> elements, int idx)
		{
			if (idx > -1 && idx < elements.Count)
			{
				elements.RemoveAt(idx);

				List<object> items = new List<object>(this._seed.Items);
				items.RemoveAt(idx);

				this._seed.Items = items.ToArray();
				this._seed.ItemsElementName = elements.ToArray();
			}
		}

		/// <summary>
		/// Returns the DataSetReference object's properties and values to the last known clean state and resets the IsDirty flag to false
		/// </summary>
		public override void RestoreState()
		{
			if (this._copy != null)
			{
				this._label = this._copy.LabelField;
				this._value = this._copy.ValueField;
				this._name = this._copy.Name;
				this._target = this._copy.Target;
			}
			this.ClearState();
		}

		/// <summary>
		/// Creates a true copy of the DataSetReference object and its children
		/// </summary>
		/// <returns>A new DataSetReference object copy</returns>
		public override DataSetReference CreateDeepCopy()
		{
			if (this._copy != null)
			{
				return this._copy;
			}
			return new DataSetReference(this);
		}

		/// <summary>
		/// Gets the underlying seed object which was originally deserialized from the RDL XML and which is used to
		/// ensure that changes made to the DataSetReference object are written back to the original file when saved.
		/// </summary>
		/// <returns>DataSetReferenceType</returns>
		public DataSetReferenceType GetSeedObject()
		{
			return this._seed;
		}

		#endregion

		public override object CreateSeedCopy()
		{
			DataSetReferenceType dsrt = new DataSetReferenceType();
			List<ItemsChoiceType31> elements = new List<ItemsChoiceType31>(this._seed.ItemsElementName);
			List<object> items = new List<object>(this._seed.Items);

			dsrt.ItemsElementName = elements.ToArray();
			dsrt.Items = items.ToArray();

			return dsrt;
		}
	}
}
